/* Copyright ©, 2022, ECARX (Hubei) Technology Co., Ltd. All rights reserved.
 *
 * NOTICE: This file contains information which is confidential and proprietary to ECARX
 * (Hubei) Technology Co., Ltd. and/or other developers. No license to any intellectual
 * property rights contained within this file is either granted or implied except as may be
 * provided in an agreement with ECARX (Hubei) Technology Co., Ltd.
 *
 * Any unauthorized copying or distribution of content from this file in whole or in part
 * is prohibited.
 */
#ifndef ECARX_VHAL_CORE_WRITE_PROPERTY_BUILD_H
#define ECARX_VHAL_CORE_WRITE_PROPERTY_BUILD_H

#include <functional>
#include <type_traits>
#include <vector>

#include "cstdint"
#include "vhal_core/property/TimerBuilder.h"
#include "vhal_core/utils/convert.h"
#include "VHalLog.h"

namespace ecarx::vhal::core::property {

template <typename _TY>
class WritePropertyAreaBuilder;

template <typename _TY>
class WritePropertyBuilder {
    IProperty* mProperty;

  public:
    WritePropertyBuilder(IProperty* property) : mProperty(property) {}

    WritePropertyBuilder<_TY>& mixedTypeConfig(bool oneStr, bool oneBool, bool oneInt,
                                              int32_t sizeInt, bool oneLong, int32_t sizeLong,
                                              bool oneFloat, int32_t sizeFloat, int32_t sizeByte) {
        if (std::is_same_v<_TY, MixedValue>) {
            mProperty->mixedTypeConfig(oneStr, oneBool, oneInt, sizeInt, oneLong, sizeLong,
                                       oneFloat, sizeFloat, sizeByte);
        }
        return *this;
    }

    WritePropertyBuilder<_TY>& configArray(std::vector<int32_t> array) {
        mProperty->configArray(array);
        return *this;
    }

    template <typename AREA>
    WritePropertyAreaBuilder<_TY> area(AREA&& area) {
        auto areaId = static_cast<int32_t>(area);
        constexpr const auto globalArea = toInt(VehicleArea::GLOBAL);
        return WritePropertyAreaBuilder<_TY>(new Area(mProperty, areaId == globalArea ? 0 : areaId,
                                                      VehiclePropertyStatus::AVAILABLE));
    }
};

template <typename _TY>
class WritePropertySetBuilder {
    Area* mArea;

  public:
    WritePropertySetBuilder(Area* area) : mArea(area) {}

    TimerBuilder<_TY, WritePropertyBuilder<_TY>> onSetProperty(SetFn<_TY> fn) {
        if constexpr (std::is_enum_v<_TY>) {
            mArea->setSetTask([fn](const VehiclePropValue& value) {
                if(value.value.int32Values.size() == 0){
                    VHALOG.e("prop:0x%x areaId:%d, onSetProperty int32Values size is 0, return.",
                        value.prop, value.areaId);
                    return;
                }
                fn(_TY(value.value.int32Values[0]));
            });
        } else {
            mArea->setSetTask([fn](const VehiclePropValue& value) {
                fn(utils::toValue<_TY>(value.value.bytes));
            });
        }
        return TimerBuilder<_TY, WritePropertyBuilder<_TY>>(mArea);
    }
};

template <typename _TY>
class WritePropertyAreaBuilder {
    Area* mArea;

  public:
    WritePropertyAreaBuilder(Area* area) : mArea(area) {}

    WritePropertyAreaBuilder<_TY>& range(_TY min __attribute__((unused)),
                                         _TY max __attribute__((unused))) {
        if constexpr (std::is_enum_v<_TY>) {
            mArea->getAreaConfig().minInt32Value = static_cast<int32_t>(min);
            mArea->getAreaConfig().maxInt32Value = static_cast<int32_t>(max);
        }
        return *this;
    }

    WritePropertySetBuilder<_TY> immutableStatus(PropertyStatus status) {
        mArea->updateStatus(status);
        return WritePropertySetBuilder<_TY>(mArea);
    }

    template <typename... SIGNALS>
    WritePropertySetBuilder<_TY> withStatusBySignals(PropStatusFn&& statusFn, SIGNALS... signal) {
        auto signalCallback = [statusFn](Area* area, int64_t timestamp) {
            PropertyStatus newStatus = statusFn(area->getStatus());
            area->updateStatus(newStatus);
        };
        mArea->setStatusFn([statusFn](Area* area) {
            return statusFn(area->getStatus());
        });
        (mArea->registerSignalCallback(toInt(signal), signalCallback), ...);
        return WritePropertySetBuilder<_TY>(mArea);
    }
};

template <>
inline TimerBuilder<bool, WritePropertyBuilder<bool>> WritePropertySetBuilder<bool>::onSetProperty(
    SetFn<bool> fn) {
    mArea->setSetTask(
        [fn](const VehiclePropValue& value) {
            if(value.value.int32Values.size() == 0){
                VHALOG.e("prop:0x%x areaId:%d, onSetProperty int32Values size is 0, return.",
                    value.prop, value.areaId);
                return;
            }
            fn(value.value.int32Values[0] == 1);
        });
    return TimerBuilder<bool, WritePropertyBuilder<bool>>(mArea);
}

template <>
inline TimerBuilder<int32_t, WritePropertyBuilder<int32_t>>
WritePropertySetBuilder<int32_t>::onSetProperty(SetFn<int32_t> fn) {
    mArea->setSetTask(
        [fn](const VehiclePropValue& value) {
            if(value.value.int32Values.size() == 0){
                VHALOG.e("prop:0x%x areaId:%d, onSetProperty int32Values size is 0, return.",
                    value.prop, value.areaId);
                return;
            }
            fn(value.value.int32Values[0]);
        });
    return TimerBuilder<int32_t, WritePropertyBuilder<int32_t>>(mArea);
}

template <>
inline TimerBuilder<std::vector<int32_t>, WritePropertyBuilder<std::vector<int32_t>>>
WritePropertySetBuilder<std::vector<int32_t>>::onSetProperty(SetFn<std::vector<int32_t>> fn) {
    mArea->setSetTask([fn](const VehiclePropValue& value) { fn(value.value.int32Values); });
    return TimerBuilder<std::vector<int32_t>, WritePropertyBuilder<std::vector<int32_t>>>(mArea);
}

template <>
inline TimerBuilder<int64_t, WritePropertyBuilder<int64_t>>
WritePropertySetBuilder<int64_t>::onSetProperty(SetFn<int64_t> fn) {
    mArea->setSetTask(
        [fn](const VehiclePropValue& value) {
            if(value.value.int64Values.size() == 0){
                VHALOG.e("prop:0x%x areaId:%d, onSetProperty int64Values size is 0, return.",
                    value.prop, value.areaId);
                return;
            }
            fn(value.value.int64Values[0]);
        });
    return TimerBuilder<int64_t, WritePropertyBuilder<int64_t>>(mArea);
}

template <>
inline TimerBuilder<std::vector<int64_t>, WritePropertyBuilder<std::vector<int64_t>>>
WritePropertySetBuilder<std::vector<int64_t>>::onSetProperty(SetFn<std::vector<int64_t>> fn) {
    mArea->setSetTask([fn](const VehiclePropValue& value) { fn(value.value.int64Values); });
    return TimerBuilder<std::vector<int64_t>, WritePropertyBuilder<std::vector<int64_t>>>(mArea);
}

template <>
inline TimerBuilder<float, WritePropertyBuilder<float>>
WritePropertySetBuilder<float>::onSetProperty(SetFn<float> fn) {
    mArea->setSetTask(
        [fn](const VehiclePropValue& value) {
            if(value.value.floatValues.size() == 0){
                VHALOG.e("prop:0x%x areaId:%d, onSetProperty floatValues size is 0, return.",
                    value.prop, value.areaId);
                return;
            }
            fn(value.value.floatValues[0]);
        });
    return TimerBuilder<float, WritePropertyBuilder<float>>(mArea);
}

template <>
inline TimerBuilder<std::vector<float>, WritePropertyBuilder<std::vector<float>>>
WritePropertySetBuilder<std::vector<float>>::onSetProperty(SetFn<std::vector<float>> fn) {
    mArea->setSetTask([fn](const VehiclePropValue& value) { fn(value.value.floatValues); });
    return TimerBuilder<std::vector<float>, WritePropertyBuilder<std::vector<float>>>(mArea);
}

template <>
inline TimerBuilder<std::string, WritePropertyBuilder<std::string>>
WritePropertySetBuilder<std::string>::onSetProperty(SetFn<std::string> fn) {
    mArea->setSetTask([fn](const VehiclePropValue& value) { fn(value.value.stringValue); });
    return TimerBuilder<std::string, WritePropertyBuilder<std::string>>(mArea);
}

template <>
inline TimerBuilder<std::vector<uint8_t>, WritePropertyBuilder<std::vector<uint8_t>>>
WritePropertySetBuilder<std::vector<uint8_t>>::onSetProperty(SetFn<std::vector<uint8_t>> fn) {
    mArea->setSetTask([fn](const VehiclePropValue& value) { fn(value.value.bytes); });
    return TimerBuilder<std::vector<uint8_t>, WritePropertyBuilder<std::vector<uint8_t>>>(mArea);
}

template <>
inline TimerBuilder<MixedValue, WritePropertyBuilder<MixedValue>>
WritePropertySetBuilder<MixedValue>::onSetProperty(SetFn<MixedValue> fn) {
    mArea->setSetTask([fn](const VehiclePropValue& value) { fn(value.value); });
    return TimerBuilder<MixedValue, WritePropertyBuilder<MixedValue>>(mArea);
}

template <>
inline WritePropertyAreaBuilder<int32_t>& WritePropertyAreaBuilder<int32_t>::range(int32_t min,
                                                                                   int32_t max) {
    mArea->getAreaConfig().minInt32Value = min;
    mArea->getAreaConfig().maxInt32Value = max;
    return *this;
}

template <>
inline WritePropertyAreaBuilder<int64_t>& WritePropertyAreaBuilder<int64_t>::range(int64_t min,
                                                                                   int64_t max) {
    mArea->getAreaConfig().minInt64Value = min;
    mArea->getAreaConfig().maxInt64Value = max;
    return *this;
}

template <>
inline WritePropertyAreaBuilder<float>& WritePropertyAreaBuilder<float>::range(float min,
                                                                               float max) {
    mArea->getAreaConfig().minFloatValue = min;
    mArea->getAreaConfig().maxFloatValue = max;
    return *this;
}

}  // namespace swc::property

#endif